package com.spa.application.service.massagist;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.spa.application.command.massagist.WithdrawAccountCommand;
import com.spa.application.command.massagist.WithdrawApplyNewCommand;
import com.spa.application.config.massagist.ProfitRule;
import com.spa.application.dto.PageResult;
import com.spa.application.dto.massagist.*;
import com.spa.application.query.massagist.MassagistOrderWithdrawQuery;
import com.spa.application.query.massagist.MassagistWithdrawOrderDetailQuery;
import com.spa.application.query.massagist.MessagistWithdrawQuery;
import com.spa.application.query.massagist.MessagistWithdrawRecordDetailQuery;
import com.spa.domain.entity.MassagistWithdrawRecord;
import com.spa.domain.entity.OrderInfo;
import com.spa.domain.entity.Profit;
import com.spa.domain.entity.Shop;
import com.spa.domain.service.MassagistWithdrawRecordService;
import com.spa.domain.service.OrderService;
import com.spa.domain.service.ShopService;
import com.spa.domain.service.WithdrawAccountService;
import com.spa.infrastructure.enums.dict.UserTypeEnum;
import com.spa.infrastructure.enums.dict.order.OrderOperStatusEnum;
import com.spa.infrastructure.enums.dict.order.OrderStatusEnum;
import com.spa.infrastructure.exception.SpaException;
import com.spa.infrastructure.mapper.MassagistWithdrawRecordMapperStruct;
import com.spa.infrastructure.mapper.OrderMapperStruct;
import com.spa.infrastructure.mapper.WithdrawAccountMapperStruct;
import com.spa.infrastructure.repository.condition.MassagistWithdrawRecordCondition;
import com.spa.infrastructure.repository.condition.OrderInfoCondition;
import com.spa.infrastructure.repository.condition.WithdrawAccountCondition;
import com.spa.infrastructure.repository.po.MassagistWithdrawRecordPO;
import com.spa.infrastructure.repository.po.OrderInfoPO;
import com.spa.infrastructure.util.DateUtil;
import com.spa.infrastructure.util.RedisUtil;
import com.spa.infrastructure.util.SessionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

@Service
@Slf4j
public class MassagistWithdrawApplicationService {

    @Autowired
    private MassagistWithdrawRecordService massagistWithdrawRecordService;

    @Autowired
    private ShopService shopService;

    @Resource
    private RedisUtil redisUtil;

    @Autowired
    private MassagistHomeApplicationService massagistHomeApplicationService;

    @Autowired
    private OrderService orderService;

    @Resource
    private WithdrawAccountService withdrawAccountService;

    /**
     * 申请提现
     * @param command
     */
    @Transactional
    public void applyNew(WithdrawApplyNewCommand command) {
        log.info("WithdrawApplyNewCommand : {}", command);
        long massagistId = SessionUtil.getUser().getExtUserId();
        String lockKey = "massagist：withdraw：lock:" + massagistId;
        String id = UUID.fastUUID().toString(true);
        try {
            boolean lockFlag = redisUtil.getLock(lockKey, id);
            if (!lockFlag) {
                throw new SpaException("操作频繁！");
            }
            if (command.getAmount() <= 0) {
                throw new SpaException("提现金额不能为0！");
            }
            if (massagistHomeApplicationService.getCashAvailable() < command.getAmount()) {
                throw new SpaException("余额不足！");
            }
            // 查询提现的订单列表
            List<OrderInfo> orderInfoList;
            OrderInfoCondition condition = new OrderInfoCondition();
            condition.setWithdrawMassagistId(massagistId);
            if (command.getApplyType() == 1){
                orderInfoList = orderService.orderList(condition);
            }else{
                condition.setOrderIds(command.getOrderIds());
                orderInfoList = orderService.orderList(condition);
            }
            // 检测是否有已提现的订单，防止重复提现
            if(!checkOrderListIsWithdraw(orderInfoList) || CollectionUtil.isEmpty(orderInfoList)){
                throw new SpaException("提现失败，请稍后再试！");
            }
            MassagistWithdrawRecordPO po = MassagistWithdrawRecordMapperStruct.INSTANCE.commandApply2Po(command);
            // 技师姓名
            po.setMassagistName(SessionUtil.getUser().getMassagist().getName());
            po.setMassagistId(massagistId);
            po.setShopId(SessionUtil.getUser().getShopId());
            // 门店名称
            Shop shop = shopService.getShopById(SessionUtil.getUser().getShopId());
            po.setShopName(shop.getName());
            massagistWithdrawRecordService.add(po);
            // 提现成功之后，更新提现账户
            WithdrawAccountCommand withdrawAccountCommand = getWithdrawAccountCommand(command, massagistId);
            withdrawAccountService.upsertWithdrawAccount(WithdrawAccountMapperStruct.INSTANCE.command2Entity(withdrawAccountCommand));
            // 提现成功之后，更新订单
            if (orderInfoList != null) {
                updateWithdraw(orderInfoList, po.getId());
            }
        } finally {
            redisUtil.releaseLock(lockKey, id);
        }
    }

    /**检测是否有已提现的订单*/
    private boolean checkOrderListIsWithdraw(List<OrderInfo> orderInfoList) {
        if(CollectionUtil.isNotEmpty(orderInfoList)){
            for (OrderInfo orderInfo : orderInfoList) {
                if(orderInfo.getMassagistWithdrawId()!=null){
                    return false;
                }
            }
        }
        return true;
    }
    /** 获取提现账户 */
    private static WithdrawAccountCommand getWithdrawAccountCommand(WithdrawApplyNewCommand command, long massagistId) {
        WithdrawAccountCommand withdrawAccountCommand =new WithdrawAccountCommand();
        withdrawAccountCommand.setType(UserTypeEnum.$10.getCode());
        withdrawAccountCommand.setMainId(massagistId);
        withdrawAccountCommand.setBankName(command.getRealName());
        withdrawAccountCommand.setBankAccount(command.getBankNo());
        withdrawAccountCommand.setBankDetail(command.getBankDetail());
        withdrawAccountCommand.setBankNumber(command.getBankNumber());
        withdrawAccountCommand.setBankPic(command.getBankPic());
        return withdrawAccountCommand;
    }

    private void updateWithdraw(List<OrderInfo> orderInfos, long massagistWithdrawId) {
        for (OrderInfo orderInfo : orderInfos) {
            OrderInfoPO po = new OrderInfoPO();
            po.setId(orderInfo.getId());
            po.setMassagistWithdrawId(massagistWithdrawId);
            orderService.updateOrder(po);
        }
    }

    /**
     * 获取技师的上一次提现记录
     */
    private MassagistWithdrawRecord getLastWithdrawRecord(long massagistId) {
        MassagistWithdrawRecordCondition condition = new MassagistWithdrawRecordCondition();
        condition.setMassagistId(massagistId);
        List<MassagistWithdrawRecord> massagistWithdrawRecords = massagistWithdrawRecordService.list(condition);
        if (massagistWithdrawRecords.isEmpty()) {
            return null;
        }
        return massagistWithdrawRecords.get(0);
    }

    /**
     * 获取此次提现涉及到的订单（状态是已完成和已出发后取消涉及到车费的）
     * 订单的创建时间是在上一次发起提现时间之后的
     */
    private List<OrderInfo> getApplyWithdrawOrderList(long massagistId, String beginDate) {
        OrderInfoCondition condition = new OrderInfoCondition();
        condition.setMassagistId(massagistId);
        condition.setBeginDate(beginDate);
        List<OrderInfo> orderInfos = orderService.orderList(condition);
        List<OrderInfo> complete = orderInfos.stream().filter(orderInfo -> Objects.equals(orderInfo.getStatus(), OrderStatusEnum.$40.getCode())).toList();
        List<OrderInfo> cancel = orderInfos.stream().filter(orderInfo -> Objects.equals(orderInfo.getStatus(), OrderStatusEnum.$99.getCode())
                                            && (Objects.equals(orderInfo.getOrderOperStatus(), OrderOperStatusEnum.$20.getCode())
                                                || Objects.equals(orderInfo.getOrderOperStatus(), OrderOperStatusEnum.$30.getCode()))).toList();
        List<OrderInfo> result = new ArrayList<>();
        result.addAll(cancel);
        result.addAll(complete);
        return result;
    }

    public PageResult<MessagistWithdrawListDTO> page(MessagistWithdrawQuery query) throws ParseException {
        MassagistWithdrawRecordCondition condition = MassagistWithdrawRecordMapperStruct.INSTANCE.query2Condition(query);
        condition.setBeginTime(DateUtil.date2Str(DateUtil.getStartTimeOfCurrentMonth(DateUtil.str2Date(query.getApplyTime(), "yyyy-MM"))));
        condition.setEndTime(DateUtil.date2Str(DateUtil.getEndTimeOfCurrentMonth(DateUtil.str2Date(query.getApplyTime(), "yyyy-MM"))));
        condition.setMassagistId(SessionUtil.getUser().getExtUserId());
        Page<MassagistWithdrawRecord> page = massagistWithdrawRecordService.page(condition);
        return MassagistWithdrawRecordMapperStruct.INSTANCE.entity2MessagistWithdrawListDto4Page(page);
    }

    public MessagistWithdrawProgressDTO progress(MessagistWithdrawQuery query) {
        return MassagistWithdrawRecordMapperStruct.INSTANCE.entity2MessagistWithdrawProgressDto(massagistWithdrawRecordService.getDetailById(query.getMessagistWithdrawId()));
    }


    /**
     * 技师提现分页列表
     * @return
     */
    public PageResult<OrderListWithdrawDTO> orderList(MassagistOrderWithdrawQuery query) {
        long massagistId = SessionUtil.getUser().getExtUserId();
        OrderInfoCondition condition = MassagistWithdrawRecordMapperStruct.INSTANCE.query2OrderInfoWithdrawCondition(query);
        condition.setWithdrawMassagistId(massagistId);
        OrderService.OrderInfoPlug plug = OrderService.OrderInfoPlug.empty();
        plug.setProfit(true);
        Page<OrderInfo> orderInfoPage = orderService.pageByCondition(condition, plug);
        // 计算每笔订单的分佣合计
        List<OrderProfitInfo> orderProfitInfoList = calculateOrderProfit(orderInfoPage.getRecords());
        PageResult<OrderListWithdrawDTO> result = OrderMapperStruct.INSTANCE.entity2OrderListWithdrawDto4Page(orderInfoPage);
        result.getRecords().forEach(orderListWithdrawDTO -> {
            OrderProfitInfo orderProfitInfo = orderProfitInfoList.stream().filter(item -> item.getOrderId().equals(orderListWithdrawDTO.getId())).findFirst().orElse(null);
            if (orderProfitInfo!=null){
                orderListWithdrawDTO.setMassagistIncome(orderProfitInfo.getMassagistIncome());
                orderListWithdrawDTO.setTravelPrice(orderProfitInfo.getTravelPrice());
                orderListWithdrawDTO.setAmount(orderListWithdrawDTO.getMassagistIncome()+orderListWithdrawDTO.getTravelPrice());
            }
        });
        return result;
    }


    /**
     * 技师提现记录对应的订单分页列表
     * @param query
     * @return
     */
    public PageResult<OrderListWithdrawDTO> withdrawOrderList(MessagistWithdrawRecordDetailQuery query) {
        OrderInfoCondition condition = MassagistWithdrawRecordMapperStruct.INSTANCE.messagistWithdrawRecordDetailQuery2OrderInfoCondition(query);
        OrderService.OrderInfoPlug plug = OrderService.OrderInfoPlug.empty();
        plug.setProfit(true);
        Page<OrderInfo> orderInfoPage = orderService.pageByCondition(condition, plug);
        // 计算每笔订单的分佣合计
        List<OrderProfitInfo> orderProfitInfoList = calculateOrderProfit(orderInfoPage.getRecords());
        PageResult<OrderListWithdrawDTO> result = OrderMapperStruct.INSTANCE.entity2OrderListWithdrawDto4Page(orderInfoPage);
        result.getRecords().forEach(orderListWithdrawDTO -> {
            OrderProfitInfo orderProfitInfo = orderProfitInfoList.stream().filter(item -> item.getOrderId().equals(orderListWithdrawDTO.getId())).findFirst().orElse(null);
            if (orderProfitInfo!=null){
                orderListWithdrawDTO.setMassagistIncome(orderProfitInfo.getMassagistIncome());
                orderListWithdrawDTO.setTravelPrice(orderProfitInfo.getTravelPrice());
                orderListWithdrawDTO.setAmount(orderListWithdrawDTO.getMassagistIncome()+orderListWithdrawDTO.getTravelPrice());
            }
        });
        return result;
    }

    /**
     * 技师提现订单详情
     * @param query
     * @return
     */
    public MassagistWithdrawOrderDetailDTO orderDetail(MassagistWithdrawOrderDetailQuery query) {
        OrderService.OrderInfoPlug plug = OrderService.OrderInfoPlug.empty();
        plug.setOrderExt(true);
        plug.setProfit(true);
        plug.setTicket(true);
        OrderInfo orderInfo = orderService.getOrderInfoById(query.getOrderId(), plug);
        if (ObjectUtil.isNull(orderInfo)){
            throw new SpaException("订单查询失败！");
        }
        if (ObjectUtil.isNull(orderInfo) && !ObjectUtil.equal(orderInfo.getMassagistId(),SessionUtil.getUser().getExtUserId())){
            throw new SpaException("订单查询失败！");
        }
        MassagistWithdrawOrderDetailDTO massagistWithdrawOrderDetailDTO = OrderMapperStruct.INSTANCE.entity2MassagistWithdrawOrderDetailDTO(orderInfo);
        // 计算每笔订单的分佣合计
        List<OrderProfitInfo> orderProfitInfoList = calculateOrderProfit(Collections.singletonList(orderInfo));
        OrderProfitInfo orderProfitInfo = orderProfitInfoList.stream().filter(item -> item.getOrderId().equals(orderInfo.getId())).findFirst().orElse(null);
        if (orderProfitInfo!=null){
            massagistWithdrawOrderDetailDTO.setTotalIncome(orderProfitInfo.getMassagistIncome()+orderProfitInfo.getTravelPrice());
        }
        // 利润数据里面的提成比例拿出来
        List<Profit> profitList = orderInfo.getProfitList();
        // 订单分佣金额和比例
        List<OrderProfitRateInfo> orderProfitRateInfoList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(profitList)){
            profitList.forEach(profit -> {
                String ruleData = profit.getRuleData();
                if (ruleData!=null){
                    ProfitRule profitRule = JSONUtil.toBean(profit.getRuleData(), ProfitRule.class);
                    OrderProfitRateInfo orderProfitRateInfo =  new OrderProfitRateInfo();
                    orderProfitRateInfo.setOrderId(profit.getOrderId());
                    orderProfitRateInfo.setOrderType(profit.getOrderType());
                    orderProfitRateInfo.setIncome(profit.getMassagistIncome());
                    orderProfitRateInfo.setRate(Convert.toDouble(profitRule.getMassagistPercent(), 0D));
                    if (profit.getOrderType() == 1 || profit.getOrderType() == 3){
                        orderProfitRateInfo.setOrderId(profit.getOrderId());
                    }else if (profit.getOrderType() == 2 && profit.getExtendOrderId()!=null){
                        orderProfitRateInfo.setOrderId(profit.getExtendOrderId());
                    }
                    orderProfitRateInfoList.add(orderProfitRateInfo);
                }
            });
        }
        // 订单分佣金额、分佣比例赋值
        OrderProfitRateInfo orderProfitRateInfo = orderProfitRateInfoList.stream().filter(item->item.getOrderId().equals(orderInfo.getId()) && item.getOrderType() ==1).findFirst().orElse(null);
        massagistWithdrawOrderDetailDTO.setPercent(orderProfitRateInfo!=null?orderProfitRateInfo.getRate():null);
        massagistWithdrawOrderDetailDTO.setIncome(orderProfitRateInfo!=null? (float) orderProfitRateInfo.getIncome() :null);
        // 加钟订单分佣比例赋值
        List<MassagistWithdrawOrderDetailDTO.Ext> extList = massagistWithdrawOrderDetailDTO.getExtList();
        if (extList!=null){
            extList.forEach(ext -> {
                OrderProfitRateInfo orderProfitRateInfoExt = orderProfitRateInfoList.stream().filter(item->item.getOrderId().equals(ext.getId()) && item.getOrderType() ==2).findFirst().orElse(null);
                if (orderProfitRateInfoExt==null){
                    orderProfitRateInfoExt = orderProfitRateInfoList.stream().filter(item->item.getOrderId().equals(ext.getOrderId()) && item.getOrderType() ==2).findFirst().orElse(null);
                }
                if (orderProfitRateInfoExt!=null){
                    ext.setPercent(orderProfitRateInfoExt.getRate());
                    ext.setIncome((float)orderProfitRateInfoExt.getIncome());
                }
            });
        }
        return massagistWithdrawOrderDetailDTO;
    }

    /**
     * 技师提现总额
     * @return
     */
    public Integer orderTotal() {
        Integer orderTotal = 0;
        long massagistId = SessionUtil.getUser().getExtUserId();
        OrderInfoCondition condition = new OrderInfoCondition();
        condition.setWithdrawMassagistId(massagistId);
        List<OrderInfo> orderInfoList = orderService.orderList(condition);
        // 计算每笔订单的分佣合计
        List<OrderProfitInfo> orderProfitInfoList = calculateOrderProfit(orderInfoList);
        orderTotal =orderProfitInfoList.stream().map(OrderProfitInfo::getMassagistIncome).reduce(Integer::sum).orElse(0)+orderProfitInfoList.stream().map(OrderProfitInfo::getTravelPrice).reduce(Integer::sum).orElse(0);
        return orderTotal;
    }

    /**
     * 技师最新提现账户
     * @return
     */
    public WithdrawAccountDTO lastWithdrawAccount() {
        long massagistId = SessionUtil.getUser().getExtUserId();
        WithdrawAccountCondition condition = new WithdrawAccountCondition();
        condition.setType(UserTypeEnum.$10.getCode());
        condition.setMainId(massagistId);
        return WithdrawAccountMapperStruct.INSTANCE.entity2DTO(withdrawAccountService.getWithdrawAccountPOLast(condition));
    }

    /**
     * 计算每笔订单的分佣合计
     *
     * @param orderInfos
     * @return
     */
    private List<OrderProfitInfo> calculateOrderProfit(List<OrderInfo> orderInfos) {
        List<OrderProfitInfo> profitInfoList = new ArrayList<>();
        for (OrderInfo orderInfo : orderInfos) {
            int massagistIncome = 0;
            int travelPrice = 0;
            int shopIncome = 0;
            if (orderInfo.getProfitList() != null) {
                massagistIncome = orderInfo.getProfitList().stream()
                        .mapToInt(profit -> profit.getMassagistIncome() != null ? profit.getMassagistIncome() : 0)
                        .sum();

                travelPrice = orderInfo.getProfitList().stream()
                        .mapToInt(profit -> profit.getTravelPrice() != null ? profit.getTravelPrice() : 0)
                        .sum();

                shopIncome = orderInfo.getProfitList().stream()
                        .mapToInt(profit -> profit.getShopIncome() != null ? profit.getShopIncome() : 0)
                        .sum();
            }
            OrderProfitInfo orderProfitInfo = new OrderProfitInfo(orderInfo.getId(), massagistIncome, travelPrice);
            profitInfoList.add(orderProfitInfo);
        }
        return profitInfoList;
    }
}
